#include "rrcuboid.h"
#include <cmath>

RRCuboid::RRCuboid()
{
    type = RR_CUBOID;
}

void RRCuboid::rrTrace(RRRay *ray, RRCrossInfo *arg)
{
    RRRange rng;
    rng.s = 1.0e+18;
    rng.t = 1.0e+20;
    arg->rrSetRange(rng);
    arg->rrSetHit(false);
    rrHitSurface(ray, RR_UPPER, arg);
    rrHitSurface(ray, RR_DOWN, arg);
    rrHitSurface(ray, RR_FRONT, arg);
    rrHitSurface(ray, RR_REAR, arg);
    rrHitSurface(ray, RR_RIGHT, arg);
    rrHitSurface(ray, RR_LEFT, arg);
    return;
}

void RRCuboid::rrHitSurface(RRRay *ray, RRSURFACE dir, RRCrossInfo *arg)
{
    RRRange rng;
    rng = arg->rrGetRange();
    RRVec3 d = ray->rrGetDir();
    RRVec3 r = ray->rrGetSrc();
    RRVec3 N;
    RRVec3 P[4];
    double denomi, numera, dis_temp, t;
    RRVec3 tosrc_temp, C, cross_temp;
    //頂点, 法線の初期化
    if(dir == RR_UPPER) {
        N = RRSetVec3(0.0f, -1.0f, 0.0f);
        P[0] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
        P[1] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
        P[2] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
        P[3] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
    }
    else if(dir == RR_DOWN) {
        N = RRSetVec3(0.0f, 1.0f, 0.0f);
        P[0] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[1] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[2] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[3] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
    }
    else if(dir == RR_FRONT) {
        N = RRSetVec3(0.0f, 0.0f, -1.0f);
        P[0] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
        P[1] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[2] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[3] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
    }
    else if(dir == RR_REAR) {
        N = RRSetVec3(0.0f, 0.0f, 1.0f);
        P[0] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
        P[1] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[2] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[3] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
    }
    else if(dir == RR_RIGHT) {
        N = RRSetVec3(1.0f, 0.0f, 0.0f);
        P[0] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
        P[1] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[2] = RRSetVec3(coord.x + size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[3] = RRSetVec3(coord.x + size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
    }
    else if(dir == RR_LEFT) {
        N = RRSetVec3(-1.0f, 0.0f, 0.0f);
        P[0] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z + size.z / 2.0f);
        P[1] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z + size.z / 2.0f);
        P[2] = RRSetVec3(coord.x - size.x / 2.0f, coord.y + size.y / 2.0f, coord.z - size.z / 2.0f);
        P[3] = RRSetVec3(coord.x - size.x / 2.0f, coord.y - size.y / 2.0f, coord.z - size.z / 2.0f);
    }
    denomi = N.x * d.x + N.y * d.y + N.z * d.z;
    //交点の計算
    if(denomi == 0) return;
    else {
        numera = N.x * (P[0].x - r.x) + N.y * (P[0].y - r.y) + N.z * (P[0].z - r.z);
        t = numera / denomi;
        if(t <= 1.0e-4) return;
        else {
            C = RRSetVec3(r.x + t * d.x, r.y + t * d.y, r.z + t * d.z);
            tosrc_temp = RRSetVec3(-t * d.x, -t * d.y, -t * d.z);
            tosrc_temp = RRNormalize(tosrc_temp);
            dis_temp =  RRDistance(r, C);
            if(dis_temp > rng.t) return;
        }
    }
    //ポリゴン内かのテスト
    for(int i=0; i<4; i++) {
        RRVec3 PC, PP, NN;
        float diff_x, diff_y, diff_z;
        int j;
        if(4-i == 4) j = 0; else j = 4-i;
        PC = RRSetVec3(C.x - P[j].x, C.y - P[j].y, C.z - P[j].z);
        PP = RRSetVec3(P[3-i].x - P[j].x, P[3-i].y - P[j].y, P[3-i].z - P[j].z);
        NN = RRCrossProduct(PC, PP); NN = RRNormalize(NN);
        diff_x = fabs(NN.x - N.x);
        diff_y = fabs(NN.y - N.y);
        diff_z = fabs(NN.z - N.z);
        if(diff_x > 1.0e-3 || diff_y > 1.0e-3 || diff_z > 1.0e-3) {
            return;
        }
    }
    if(dis_temp < rng.s) {
        arg->rrSetHit(true);
        arg->rrSetNorm(N);
        arg->rrSetToSrc(tosrc_temp);
        arg->rrSetCross(C);
        rng.t = rng.s;
        rng.s = dis_temp;
        arg->rrSetRange(rng);
        return;
    }
    else {
        rng.t = dis_temp;
        arg->rrSetRange(rng);
        return;
    }
}
